package team6.gamegui;

import java.awt.*;
import java.awt.event.*;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.*;

import team6.AI;
import team6.AI.Difficulty;
import team6.AI.Player;
import team6.ai.LearnBot;
import team6.ai.MinMax;


/**
 * Runs the game, including taking user and computer input and displaying it.
 * 
 * @author Drew Havard
 * @author Drew Reagan
 */
public class GameGui extends JFrame implements ActionListener {

    /* Declare all useful widgets and variables */
	/**
	 * Something to make the compiler happy
	 */
    private static final long serialVersionUID = 1393755838970735306L;	// something to make compiler happy
    /**
     * The opponent if there is a computer player.
     * null if two human player game.
     */
    AI opponent;  		//Null if 2 human player game
    /**
     * The opponent if there are two computer players.
     * (Two computer players not implemented in this version)
     * null if one or two human player game.
     */
    AI player;  		//Null if 1 human player game
    /**
     * Set to make block the actionListener
     */
    static AtomicBoolean listen = new AtomicBoolean();
    /**
     * The current state of the othello board and other 
     * variables used to analyze the game.
     */
    GameState state;
    /**
     * The number of human players
     */
    static int numPlayers;		// Humans
    /**
     * Dimensions of the board.  As implemented, always 8.
     */
    public static final int boardDim = 8;
    /**
     * Two dimensional array of buttons representing the board.
     * Is always 8x8
     */
    static JButton buttons[][] = new JButton[boardDim][boardDim];
    JPanel topPane = new JPanel();
    JPanel boardPane = new JPanel();
    JPanel middle = new JPanel();
    JPanel flow = new JPanel();
    JPanel fullPanel = new JPanel();

    // These will be dynamically titled at creation of game
    JLabel turnLabel = new JLabel();
    JLabel playerPiecesLabel = new JLabel();
    JLabel opponentPiecesLabel = new JLabel();

    // Both players always start with two pieces
    JLabel numPlayerPieces = new JLabel("2  ");
    JLabel numOppPieces = new JLabel("2  ");
    static JButton passButton = new JButton("Pass");

    /**
     * initBoard() will apply all of the widgets to the window and
     * add the action listeners
     */
    private void initBoard() {
        /* Set up buttons and GameState */
        Player playerMatrix[][] = new Player[boardDim][boardDim];

        for (int i = 0; i < boardDim; i++) {
            for (int j = 0; j < boardDim; j++) {
                buttons[i][j] = new JButton();
                buttons[i][j].setPreferredSize(new Dimension(60, 60));
                buttons[i][j].setBackground(Color.GRAY);
                buttons[i][j].addActionListener(this);
                playerMatrix[i][j] = Player.EMPTY;
                boardPane.add(buttons[i][j]);
            }
        }

        // Initial board setup
        buttons[3][3].setBackground(Color.WHITE);
        playerMatrix[3][3] = Player.WHITE;
        buttons[4][3].setBackground(Color.BLACK);
        playerMatrix[4][3] = Player.BLACK;
        buttons[3][4].setBackground(Color.BLACK);
        playerMatrix[3][4] = Player.BLACK;
        buttons[4][4].setBackground(Color.WHITE);
        playerMatrix[4][4] = Player.WHITE;

        state.setState(playerMatrix);
        passButton.addActionListener(this);

        // Arrange graphical stuff all nice-like
        fullPanel.setLayout(new BorderLayout());
        topPane.setLayout(new FlowLayout());
        middle.setLayout(new BorderLayout());
        boardPane.setLayout(new GridLayout(boardDim, boardDim));

        topPane.add(turnLabel);
        middle.add(topPane, BorderLayout.NORTH);
        middle.add(boardPane, BorderLayout.SOUTH);
        flow.setLayout(new FlowLayout());
        flow.add(playerPiecesLabel);
        flow.add(numPlayerPieces);
        flow.add(opponentPiecesLabel);
        flow.add(numOppPieces);
        //flow.add(message);
        flow.add(passButton);
        fullPanel.add(middle, BorderLayout.NORTH);
        fullPanel.add(flow, BorderLayout.SOUTH);
        add(fullPanel);

        // Center and finalize
        pack();
        setLocationRelativeTo(null);

        setTitle("Team 6 Othello");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    /**
     * Creates the GameState and decides all information about players,
     * AI, and difficulty levels.
     * @return Returns true if all items were initialized correctly, otherwise false.
     */
    private boolean chooseGameType() {
        state = new GameState();
        Difficulty diff;
        int diffNum = 0;
        int aiNum = 0;

        Object[] playerOptions = {
            "Player v. Player",
            "Player v. Computer"
        //"Computer v. Computer"
        };

        Object[] diffOptions = {
            "Easy",
            "Normal",
            "Hard"
        };

        Object[] botOptions = {
            "MinMax",
            "LearnBotA"
        };

        /* Code for the JOptionPanes based mostly on sample code from
         * http://java.sun.com/docs/books/tutorial/uiswing/components/dialog.html
         * "Creating and Showing Simple Dialogs"
         */
        int playType = JOptionPane.showOptionDialog(this, "Choose your play type!", "Choose!",
                JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, playerOptions, null);
        if (playType > 0) {
            aiNum = JOptionPane.showOptionDialog(this, "Choose your AI type", "Choose AI", JOptionPane.DEFAULT_OPTION,
                    JOptionPane.QUESTION_MESSAGE, null, botOptions, null);
            if (aiNum == 0) {
                diffNum = JOptionPane.showOptionDialog(this, "Choose your difficulty!", "Choose!",
                        JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, diffOptions, diffOptions[2]);
                if (diffNum < 0) {
                    System.exit(0);
                }
            }
            if (aiNum < 0) {
                System.exit(0);
            }
        } else if (playType != 0) {
            System.exit(0);
        }

        if (diffNum == 0) {
            diff = Difficulty.EASY;
        } else if (diffNum == 1) {
            diff = Difficulty.MEDIUM;
        } else {
            diff = Difficulty.HARD;
        }

        String plName = "", oppName = "";


        switch (playType) {
            // PvP
            case 0:
                opponent = null;
                player = null;
                // Determine player name
                plName = (String) JOptionPane.showInputDialog(this, "Type in Player 1's Name:", "Name Player 1!",
                        JOptionPane.PLAIN_MESSAGE, null, null, null);
                if (plName == null) {
                    System.exit(0);
                }
                oppName = (String) JOptionPane.showInputDialog(this, "Type in Player 2's Name:", "Name Player 2!",
                        JOptionPane.PLAIN_MESSAGE, null, null, null);
                if (oppName == null) {
                    System.exit(0);
                }
                if (plName.trim().equals("")) {
                    state.plName = "Player 1";
                } else {
                    state.plName = plName;
                }
                if (oppName.trim().equals("")) {
                    state.oppName = "Player 2";
                } else {
                    state.oppName = oppName;
                }

                turnLabel.setText("(Black) " + state.plName + "'s Turn");
                playerPiecesLabel.setText(state.plName + "'s pieces:");
                opponentPiecesLabel.setText(state.oppName + "'s pieces");
                numPlayers = 2;
                listen.set(true);
                return true;
            // PvCP
            case 1:
                // Instantiate AI
                if (aiNum == 0) {
                    opponent = new MinMax(Player.WHITE, diff);
                } else if (aiNum == 1) {
                    opponent = new LearnBot(Player.WHITE);
                }
                player = null;

                // Determine name
                plName = (String) JOptionPane.showInputDialog(this, "Type in Player 1's Name:", "Name Player 1",
                        JOptionPane.PLAIN_MESSAGE, null, null, null);
                if (plName == null) {
                    System.exit(0);
                }
                if (plName.trim().equals("")) {
                    state.plName = "Player";
                } else {
                    state.plName = plName;
                }
                state.oppName = "Computer";
                turnLabel.setText("(Black) " + state.plName + "'s Turn");
                playerPiecesLabel.setText(state.plName + "'s pieces:");
                opponentPiecesLabel.setText(state.oppName + "'s pieces");
                numPlayers = 1;
                listen.set(true);
                return true;
            // CPvCP
            case 2:
                //opponent = new AI(Player.WHITE,diff);
                //player = new AI(Player.BLACK);
                state.plName = "Computer 1";
                state.oppName = "Computer 2";
                turnLabel.setText("(Black) " + state.plName + "'s Turn");
                playerPiecesLabel.setText(state.plName + "s pieces: ");
                opponentPiecesLabel.setText(state.oppName + "'s pieces: ");
                numPlayers = 0;
                listen.set(false);
                return true;
            // User closed window. Assume they don't want to play.
            default:
                dispose();
                return false;
        }
    }
    /**
     * Constructs the GUI and sets it visible
     * @param args Not used
     */
    public static void main(String[] args) {
        // The GameGui only needs to be instantiated to play the game!
        GameGui gui = new GameGui();
        gui.setVisible(true);
    }

    /**
     * Decide game type and initialize board
     */
    public GameGui() {
        if (chooseGameType()) {
            initBoard();
            startGame();
        }
    }

    /**
     * Simply calculates the moves so that the game can begin.
     */
    public void startGame() {
        state.calculateMoves();
    }

    /**
     * Performs one turn for a human player
     * @param row The row in which to place the new piece
     * @param column The column in which to place the new piece
     */
    private void doTurn(int row, int column) {
        // Perform move operations and subsequent checks
        if (state.move(row, column)) {
            numPlayerPieces.setText(Integer.toString(state.plPieces) + "  ");
            numOppPieces.setText(Integer.toString(state.oppPieces) + "  ");
            turnLabel.setText(state.switchTurn());  // Switches turn and changes label in one line
            state.calculateMoves();
            if (state.needsToPass()) {
                if (numPlayers == 2) {
                    JOptionPane.showMessageDialog(this, state.turn + " must pass.");
                    listen.set(true);
                    return;
                } else if (numPlayers == 1) {
                    JOptionPane.showMessageDialog(this, "Computer must pass.");
                    state.switchTurn();
                    listen.set(true);
                    return;
                }
            }

            // End game if necessary
            checkGameOver();

            // Perform a turn for the AI, if necessary
            doAITurn();
        }
    }

    /**
     * This method is called for a computer turn and is recursively called for every time
     * the human must pass.
     */
    private void doAITurn() {
        if (numPlayers < 2 && state.currentPlayer() == opponent.getPlayer()) {
            listen.set(false);
            int[] move = opponent.makeMove(state.parse(), findBlackPiece());
            if (state.move(move[0], move[1])) {
                numPlayerPieces.setText(Integer.toString(state.plPieces) + "  ");
                numOppPieces.setText(Integer.toString(state.oppPieces) + "  ");
                turnLabel.setText(state.switchTurn());
                state.calculateMoves();
                if (state.needsToPass()) {
                    JOptionPane.showMessageDialog(this, "You must pass.");
                    turnLabel.setText(state.switchTurn());
                    move = opponent.makeMove(state.parse(), state.getLastMove());
                    doAITurn();
                }

                // End game if necessary
                checkGameOver();
            } else {
                turnLabel.setText(state.switchTurn());
                System.out.println("Turn is now " + state.turn);
                checkGameOver();
                JOptionPane.showMessageDialog(this, "Computer passed. Your turn.");
                System.out.println("Faulty move was: " + move[0] + ", " + move[1]);
            }
            listen.set(true);
        }
    }

    /**
     * Changes a button from its current state to p's
     * @param row The row in which to change the button
     * @param column The column in which to change the button
     * @param p The player to which the button is being changed
     */
    public static void changeButton(int row, int column, Player p) {
        Color c = Color.BLACK;
        if (p == Player.WHITE) {
            c = Color.WHITE;
        }
        buttons[row][column].setBackground(c);
    }
    
    
    /**
     * Returns the {row, column} position of a some black piece
     * @return {row, column} of a black piece
     */
    int[] findBlackPiece() {
    	for(int i=0; i<boardDim; i++)
    		for(int j=0; j<boardDim; j++) {
    			if(buttons[i][j].getBackground().equals(Color.BLACK)) {
    				int[] toRet = {i,j};
    				return toRet;
    			}
    		}
    	checkGameOver();
    	return null;
    }

    /**
     * Checks whether there are moves for either player.
     * If not, display the winner and exit.
     */
    public void checkGameOver() {
        String endGame = state.gameOver();
        if (!endGame.equals("")) {
            if (endGame.equals("TIE")) {
                JOptionPane.showMessageDialog(this, "Game is a tie!");
            } else {
                String name = (endGame.equals("Black")) ? state.plName : state.oppName;
                JOptionPane.showMessageDialog(this, name + " Wins!");
            }
            dispose();
            System.exit(0);
        }
    }

    public void actionPerformed(ActionEvent e) {
        /* Don't mess with input if two
         * computers are playing each other.
         */
        if (!listen.get()) {
            return;
        }

        // Check passButton
        if (e.getSource().equals(passButton)) {
            if (!state.existMoves()) {
                turnLabel.setText(state.switchTurn());
                state.calculateMoves();
                if (numPlayers < 2) {
                    int move[] = opponent.makeMove(state.parse(), state.getLastMove());
                    doTurn(move[0], move[1]);
                }

            } else {
                JOptionPane.showMessageDialog(this, "There is a valid move.");
            }
        } // Loop through board buttons
        else {
            for (int j = 0; j < boardDim; j++) {
                for (int i = 0; i < boardDim; i++) {
                    if (buttons[i][j].equals(e.getSource())) {
                        doTurn(i, j);
                    }
                }
            }
        }
    }
}
